package cn.bytepulse.web.video.service.impl;

import cn.bytepulse.pojo.AjaxResult;
import cn.bytepulse.utils.AssetsUtils;
import cn.bytepulse.utils.MinioUtils;
import cn.bytepulse.utils.ffmpeg.Ffmpeg;
import cn.bytepulse.web.admin.entity.SysAssets;
import cn.bytepulse.web.admin.mapper.SysAssetsMapper;
import cn.bytepulse.web.video.entity.VideoParts;
import cn.bytepulse.web.video.entity.Videos;
import cn.bytepulse.web.video.mapper.VideoPartsMapper;
import cn.bytepulse.web.video.mapper.VideosMapper;
import cn.bytepulse.web.video.service.VideoService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.PageInfo;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.util.*;

import static cn.bytepulse.utils.PageUtils.startPage;

/**
 * @author jiejiebiezheyang
 * @since 2025-03-21 11:00
 */
@Service
@Transactional
@RequiredArgsConstructor
public class VideoServiceImpl implements VideoService {

    private final MinioUtils minioUtils;

    private final SysAssetsMapper sysAssetsMapper;

    private final VideosMapper videosMapper;

    private final VideoPartsMapper videoPartsMapper;

    private final AssetsUtils assetsUtils;

    private final Ffmpeg ffmpeg;

    /**
     * 视频列表
     *
     * @param keyword 搜索关键字
     */
    @Override
    public AjaxResult list(String keyword) {
        QueryWrapper<Videos> wrapper = new QueryWrapper<>();
        wrapper.like(StringUtils.hasText(keyword), "title", keyword);
        wrapper.orderByDesc("create_time");
        startPage();
        List<Videos> videos = videosMapper.selectList(wrapper);
        PageInfo<Videos> pageInfo = new PageInfo<>(videos);
        return AjaxResult.success(pageInfo);
    }

    /**
     * 新增视频
     *
     * @param videos 视频信息
     */
    @Override
    public AjaxResult add(Videos videos) {
        QueryWrapper<Videos> videosQueryWrapper = new QueryWrapper<>();
        videosQueryWrapper.eq("title", videos.getTitle());
        Videos one = videosMapper.selectOne(videosQueryWrapper);
        if (one != null) {
            return AjaxResult.error("标题已存在");
        }
        videos.setId(null);
        videos.setCreateTime(new Date());
        videos.setUpdateTime(new Date());
        videosMapper.insert(videos);
        return AjaxResult.success(videos);
    }

    /**
     * 删除视频
     *
     * @param id 视频Id
     */
    @Override
    public AjaxResult delete(Integer id) throws Exception {
        QueryWrapper<VideoParts> wrapper = new QueryWrapper<>();
        wrapper.eq("video_id", id);
        List<VideoParts> videoParts = videoPartsMapper.selectList(wrapper);
        if (!videoParts.isEmpty()) {
            // 获取剧集id
            List<Integer> ids = videoParts.stream().map(VideoParts::getId).toList();
            // 查询文件
            QueryWrapper<SysAssets> assetsQueryWrapper = new QueryWrapper<>();
            assetsQueryWrapper.eq("relation_type", "video_part")
                    .in("relation_number", ids);
            List<SysAssets> sysAssets = sysAssetsMapper.selectList(assetsQueryWrapper);
            // 删除文件
            if (!sysAssets.isEmpty()) {
                List<Integer> assetsIds = sysAssets.stream().map(SysAssets::getId).toList();
                List<String> assetsObjectNames = sysAssets.stream().map(SysAssets::getObjectKey).toList();
                // 删除文件
                sysAssetsMapper.deleteBatchIds(assetsIds);
                for (String assetsObjectName : assetsObjectNames) {
                    minioUtils.deleteDirectory(assetsObjectName);
                }
            }
            // 删除剧集
            videoPartsMapper.deleteBatchIds(ids);
        }
        // 删除封面
        QueryWrapper<SysAssets> assetsQueryWrapper = new QueryWrapper<>();
        assetsQueryWrapper.eq("relation_type", "video_cover")
                .in("relation_number", id);
        SysAssets sysAssets = sysAssetsMapper.selectOne(assetsQueryWrapper);
        if (sysAssets != null) {
            sysAssetsMapper.deleteById(sysAssets.getId());
            String objectKey = sysAssets.getObjectKey();
            if (minioUtils.fileExists(objectKey)) {
                minioUtils.deleteFile(objectKey);
            }
        }
        videosMapper.deleteById(id);
        return AjaxResult.success();
    }

    /**
     * 更新视频
     *
     * @param videos 视频信息
     */
    @Override
    public AjaxResult update(Videos videos) {
        Videos one = videosMapper.selectById(videos.getId());
        if (one == null) {
            return AjaxResult.error("没有视频");
        }
        QueryWrapper<Videos> videosQueryWrapper = new QueryWrapper<>();
        videosQueryWrapper.eq("title", videos.getTitle());
        Videos videosTitle = videosMapper.selectOne(videosQueryWrapper);
        if (videosTitle != null) {
            return AjaxResult.error("标题已存在");
        }
        videos.setCreateTime(videos.getCreateTime());
        videos.setUpdateTime(new Date());
        videosMapper.updateById(videos);
        return AjaxResult.success(videos);
    }

    /**
     * 视频信息
     *
     * @param id 视频id
     */
    @Override
    public AjaxResult info(Integer id) throws Exception {
        Videos info = videosMapper.selectById(id);
        // 查询封面文件信息
        QueryWrapper<SysAssets> wrapper = new QueryWrapper<>();
        wrapper.eq("relation_type", "video_cover");
        wrapper.eq("relation_number", id);
        SysAssets sysAssets = sysAssetsMapper.selectOne(wrapper);
        if (sysAssets != null) {
            String preSignedUrl = minioUtils.preSignedUrl(sysAssets.getObjectKey(), 10);
            // 获取封面临时访问链接
            info.setCover(preSignedUrl);
        } else {
            info.setCover("");
        }
        return AjaxResult.success(info);
    }

    /**
     * 上传视频封面文件
     *
     * @param file 文件
     * @param id   视频Id
     */
    @Override
    public AjaxResult cover(MultipartFile file, Integer id) throws Exception {
        SysAssets sysAssets = assetsUtils.saveAssets("video_cover", id.toString(), "视频封面", file, true);
        return AjaxResult.success(sysAssets);
    }

    /**
     * 剧集列表
     *
     * @param id 视频id
     */
    @Override
    public AjaxResult parts(Integer id) {
        QueryWrapper<VideoParts> videoPartsQueryWrapper = new QueryWrapper<>();
        videoPartsQueryWrapper.eq("video_id", id).orderByAsc("sort");
        List<VideoParts> videoParts = videoPartsMapper.selectList(videoPartsQueryWrapper);
        return AjaxResult.success(videoParts);
    }

    /**
     * 删除剧集
     *
     * @param id 剧集id
     */
    @Override
    public AjaxResult deletePart(Integer id) throws Exception {
        QueryWrapper<SysAssets> wrapper = new QueryWrapper<>();
        wrapper.eq("relation_type", "video_part");
        wrapper.eq("relation_number", id);
        SysAssets sysAssets = sysAssetsMapper.selectOne(wrapper);

        // 判断目录是否存在
        if (sysAssets != null && minioUtils.directoryExists(sysAssets.getObjectKey())) {
            // 存在删除
            minioUtils.deleteDirectory(sysAssets.getObjectKey());
            sysAssetsMapper.deleteById(sysAssets);
        }
        videoPartsMapper.deleteById(id);
        return AjaxResult.success();
    }

    /**
     * 添加剧集
     *
     * @param videoParts 剧集信息
     */
    @Override
    public AjaxResult addPart(VideoParts videoParts) {
        // 判断视频是否存在
        QueryWrapper<Videos> videosQueryWrapper = new QueryWrapper<>();
        videosQueryWrapper.eq("id", videoParts.getVideoId());
        Videos videos = videosMapper.selectOne(videosQueryWrapper);
        if (videos == null) {
            return AjaxResult.error("视频不存在");
        }

        // 判断序号是否已存在
        QueryWrapper<VideoParts> videoPartsQueryWrapper = new QueryWrapper<>();
        videoPartsQueryWrapper.eq("video_id", videoParts.getVideoId());
        videoPartsQueryWrapper.eq("sort", videoParts.getSort());
        VideoParts parts = videoPartsMapper.selectOne(videoPartsQueryWrapper);
        if (parts != null) {
            return AjaxResult.error("序号已存在");
        }

        // 保存
        VideoParts newVideoPart = new VideoParts();
        newVideoPart.setVideoId(videoParts.getVideoId());
        newVideoPart.setTitle(videoParts.getTitle());
        newVideoPart.setSort(videoParts.getSort());
        newVideoPart.setCreateTime(new Date());
        newVideoPart.setUpdateTime(new Date());
        videoPartsMapper.insert(newVideoPart);
        // 保存资源列表
        SysAssets sysAssets = new SysAssets();
        sysAssets.setRelationType("video_part");
        sysAssets.setRelationNumber(newVideoPart.getId().toString());
        sysAssets.setFileType("dir");
        String objectKey = "videos/" + videoParts.getVideoId() + "/" + videoParts.getSort() + "/";
        sysAssets.setObjectKey(objectKey);
        sysAssets.setUploadTime(new Date());
        sysAssets.setModifyTime(new Date());
        sysAssetsMapper.insert(sysAssets);
        return AjaxResult.success(newVideoPart);
    }

    /**
     * 剧集文件列表
     *
     * @param id 剧集id
     */
    @Override
    public AjaxResult partFiles(Integer id) {
        QueryWrapper<SysAssets> assetsQueryWrapper = new QueryWrapper<>();
        assetsQueryWrapper.eq("relation_type", "video_part")
                .eq("relation_number", id);
        SysAssets sysAssets = sysAssetsMapper.selectOne(assetsQueryWrapper);
        List<String> files = new ArrayList<>();
        if (sysAssets != null) {
            String objectKey = sysAssets.getObjectKey();
            // 添加
            files = minioUtils.fileNameList(objectKey, false);
        }
        return AjaxResult.success(files);
    }

    /**
     * 删除剧集文件
     *
     * @param id       剧集id
     * @param filename 文件名
     */
    @Override
    public AjaxResult partDeleteFile(Integer id, String filename) throws Exception {
        QueryWrapper<SysAssets> assetsQueryWrapper = new QueryWrapper<>();
        assetsQueryWrapper.eq("relation_type", "video_part")
                .eq("relation_number", id);
        SysAssets sysAssets = sysAssetsMapper.selectOne(assetsQueryWrapper);
        // 删除
        if (minioUtils.fileExists(sysAssets.getObjectKey() + filename)) {
            minioUtils.deleteFile(sysAssets.getObjectKey() + filename);
        }
        return AjaxResult.success();
    }

    /**
     * 清空剧集文件
     *
     * @param id 剧集id
     */
    @Override
    public AjaxResult partClearFile(Integer id) throws Exception {
        QueryWrapper<SysAssets> assetsQueryWrapper = new QueryWrapper<>();
        assetsQueryWrapper.eq("relation_type", "video_part")
                .eq("relation_number", id);
        SysAssets sysAssets = sysAssetsMapper.selectOne(assetsQueryWrapper);
        // 清空
        if (minioUtils.directoryExists(sysAssets.getObjectKey())) {
            minioUtils.deleteDirectory(sysAssets.getObjectKey());
        }
        return AjaxResult.success();
    }

    /**
     * 上传剧集文件
     *
     * @param file 文件
     * @param id   剧集id
     */
    @Override
    public AjaxResult partUploadFile(MultipartFile file, Integer id) throws Exception {
        VideoParts videoParts = videoPartsMapper.selectById(id);
        if (videoParts == null) {
            return AjaxResult.error("剧集不存在");
        }
        // 查看资源信息
        QueryWrapper<SysAssets> wrapper = new QueryWrapper<>();
        wrapper.eq("relation_type", "video_part");
        wrapper.eq("relation_number", id);
        SysAssets sysAssets = sysAssetsMapper.selectOne(wrapper);

        // 获取项目目录
        String projectDir = System.getProperty("user.dir");
        projectDir = projectDir.endsWith("/") || projectDir.endsWith("\\") ? projectDir : projectDir + "/";
        String uuid = UUID.randomUUID() + "/";
        File tempDir = new File(projectDir + uuid);
        // 创建临时目录
        if (!tempDir.exists()) {
            tempDir.mkdirs();
        }
        // 视频文件
        File video = new File(tempDir, Objects.requireNonNull(file.getOriginalFilename()));
        file.transferTo(video);
        // 文件转码
        ffmpeg.transcodeVideo(video, projectDir + uuid);
        // 获取视频时长
        long videoDurationInSeconds = ffmpeg.getVideoDurationInSeconds(video.getAbsolutePath());
        videoParts.setDuration((double) videoDurationInSeconds);
        videoPartsMapper.updateById(videoParts);
        File[] files = tempDir.listFiles();
        if (files != null) {
            for (File f : files) {
                if (f.exists() && (f.getName().endsWith(".m3u8") || f.getName().endsWith(".ts"))) {
                    // 存到minio
                    String objectName = sysAssets.getObjectKey() + f.getName();
                    minioUtils.uploadFile(objectName, f);
                }
                f.delete();
            }
        }
        if (video.exists()) {
            video.delete();
        }
        if (tempDir.exists()) {
            tempDir.delete();
        }
        return AjaxResult.success();
    }
}
